package com.bigShen.pattern.proxy.shenProxy.source;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @author: lei.shen
 * @date: 2018/10/3
 */
public class ShenProxy {

    private static final String TMP_PROXY_CLASS_NAME = "$Proxy0";

    public static Object newProxyInstance(ShenClassLoader loader,
                                          Class<?>[] interfaces,
                                          ShenInvocationHandler h) {

        // 1. 动态生成源代码.java文件
        String javaStr = generateStringJava(interfaces);
        // 2. Java文件输出到磁盘
        String filePathMavenTarget= ShenProxy.class.getResource("") .getPath();
        String filePath = filePathMavenTarget.replace("target/classes", "src/main/java");
        //String filePath = new File("").getAbsolutePath() + "\\"
        //        + ShenProxy.class.getPackage().getName().replaceAll("\\.", "\\\\")
        //        + "\\";
        System.out.println(filePath);
        File javaFile = new File(filePath + TMP_PROXY_CLASS_NAME + ".java");
        FileWriter fw = null;

        try {

            fw = new FileWriter(javaFile);
            fw.write(javaStr);
            fw.flush();
            // 3. 把生产的.Java文件编译成.class文件
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
            Iterable iterable = manager.getJavaFileObjects(javaFile);

            JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, iterable);
            task.call();
            manager.close();

            // 4. 编译生成的.class文件加载到JVM中来
            // 类加载器ClassLoader加载到JVM
            Class proxyClass = new ShenClassLoader().findClass(TMP_PROXY_CLASS_NAME);
            Constructor con = proxyClass.getConstructor(ShenInvocationHandler.class);

            // 5. 返回字节码重组后的新的代理对象
            return con.newInstance(h);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println(javaFile.getPath()+ " 已生成.");
            // 最后删除java文件
            javaFile.delete();
        }

        return null;

    }

    private static final String LN = "\r\n";

    private static String generateStringJava(Class<?>[] interfaces) {

        StringBuffer sb = new StringBuffer();
        sb.append("package com.bigShen.pattern.proxy.shenProxy.source;" + LN);
        sb.append("import com.bigShen.pattern.proxy.Person;" + LN);
        sb.append("import java.lang.reflect.Method;" + LN);

        sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + LN);

            sb.append("ShenInvocationHandler h;" + LN);

            sb.append("public $Proxy0(ShenInvocationHandler h) {"  + LN);
                sb.append("this.h = h;");
            sb.append("}" + LN);

            for(Method infMethod : interfaces[0].getMethods()) {
                sb.append("public " + infMethod.getReturnType().getName() + " " + infMethod.getName() + "() {" + LN);
                    sb.append("try {" +LN);

                        sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\"" + infMethod.getName() + "\");" + LN);
                        sb.append("this.h.invoke(this, m, (Object[])null);" + LN);

                    sb.append("} catch(Throwable e) {" + LN);
                        sb.append("e.printStackTrace();" + LN);
                    sb.append("}" + LN);
                sb.append("}" + LN);
            }

        sb.append("}" + LN);

        return sb.toString();
    }


}
